﻿namespace NJQuery {
	using System;
	using System.ComponentModel;
	using System.Globalization;

	/// <summary>
	/// Defines an an absolute or aligned position as an effect's vanishing point.
	/// </summary>
	[TypeConverter(typeof(EffectOriginConverter))]
	public struct EffectOrigin : IEquatable<EffectOrigin>, IComplexProperty {


		#region Static Readonly Fields: Standard values

		/// <summary>
		/// Specifies the top and left positions are to be calculated to center the item horizontally and vertically.
		/// </summary>
		public static readonly EffectOrigin MiddleCenter = new EffectOrigin();

		/// <summary>
		/// Specifies the top and left positions are to be calculated to center the item horizontally align to the top.
		/// </summary>
		public static readonly EffectOrigin TopCenter = new EffectOrigin(VerticalPosition.Top, HorizontalPosition.Center);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to center the item vertically and align to the right.
		/// </summary>
		public static readonly EffectOrigin MiddleRight = new EffectOrigin(VerticalPosition.Middle, HorizontalPosition.Right);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to center the item horizontally and align to the bottom.
		/// </summary>
		public static readonly EffectOrigin BottomCenter = new EffectOrigin(VerticalPosition.Bottom, HorizontalPosition.Center);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to center the item vertically and align to the left.
		/// </summary>
		public static readonly EffectOrigin MiddleLeft = new EffectOrigin(VerticalPosition.Middle, HorizontalPosition.Left);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to align the item to the top-right corner.
		/// </summary>
		public static readonly EffectOrigin TopRight = new EffectOrigin(VerticalPosition.Top, HorizontalPosition.Right);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to align the item to the bottom-right corner.
		/// </summary>
		public static readonly EffectOrigin BottomRight = new EffectOrigin(VerticalPosition.Bottom, HorizontalPosition.Right);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to align the item to the bottom-left corner.
		/// </summary>
		public static readonly EffectOrigin BottomLeft = new EffectOrigin(VerticalPosition.Bottom, HorizontalPosition.Left);

		/// <summary>
		/// Specifies the top and left positions are to be calculated to align the item to the top-left corner.
		/// </summary>
		public static readonly EffectOrigin TopLeft = new EffectOrigin(VerticalPosition.Top, HorizontalPosition.Left);

		/// <summary>
		/// Specifies the top and left positions are to be set to the origin.
		/// </summary>
		public static readonly EffectOrigin Origin = new EffectOrigin(0, 0);

		#endregion


		#region Static Methods

		/// <summary>
		/// Converts a string representation to its <see cref="EffectOrigin" /> equivalent.
		/// </summary>
		/// <param name="text">A <see cref="String" /> containing the value to convert.</param>
		/// <param name="result">The parsed value when the conversion succeeds, or a default instance otherwise.</param>
		/// <returns>Indicates whether the conversion succeeded.</returns>
		public static bool TryParse(string text, out EffectOrigin result) {

			if(string.IsNullOrEmpty(text)) {
				result = MiddleCenter;
				return false;
			}

			string[] splitText = text.Trim().Split(',');
			if(splitText.Length != 2) {
				result = MiddleCenter;
				return false;
			}

			VerticalPosition vertical;
			if(!VerticalPosition.TryParse(splitText[0].Trim(), out vertical)) {
				result = MiddleCenter;
				return false;
			}

			HorizontalPosition horizontal;
			if(!HorizontalPosition.TryParse(splitText[1].Trim(), out horizontal)) {
				result = MiddleCenter;
				return false;
			}

			result = new EffectOrigin(vertical, horizontal);
			return true;
		}

		#endregion


		#region Fields

		private readonly HorizontalPosition horizontal;
		private readonly VerticalPosition vertical;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new <see cref="EffectOrigin" /> value.
		/// </summary>
		/// <param name="vertical">The vertical position.</param>
		/// <param name="horizontal">The horizontal position.</param>
		public EffectOrigin(VerticalPosition vertical, HorizontalPosition horizontal) {
			this.vertical = vertical;
			this.horizontal = horizontal;
		}

		/// <summary>
		/// Initializes a new <see cref="EffectOrigin" /> value using an absolute position.
		/// </summary>
		/// <param name="topPosition">The top position (in pixels).</param>
		/// <param name="leftPosition">The left position (in pixels).</param>
		public EffectOrigin(int topPosition, int leftPosition) : this(new VerticalPosition(topPosition), new HorizontalPosition(leftPosition)) { }

		/// <summary>
		/// Initializes a new <see cref="EffectOrigin" /> value using an absolute horizontal position and aligned vertical position.
		/// </summary>
		/// <param name="verticalAlign">The vertical alignment used to calculate a top position.</param>
		/// <param name="leftPosition">The left position (in pixels).</param>
		/// <exception cref="ArgumentException">Vertical align value must be defined.</exception>
		public EffectOrigin(VerticalAlign verticalAlign, int leftPosition) : this(new VerticalPosition(verticalAlign), new HorizontalPosition(leftPosition)) { }

		/// <summary>
		/// Initializes a new <see cref="EffectOrigin" /> value using an absolute vertical position and aligned horizontal position.
		/// </summary>
		/// <param name="topPosition">The top position (in pixels).</param>
		/// <param name="horizontalAlign">The horizontal alignment use to calculate a left position.</param>
		/// <exception cref="ArgumentException">Horizontal align value must be defined.</exception>
		public EffectOrigin(int topPosition, HorizontalAlign horizontalAlign) : this(new VerticalPosition(topPosition), new HorizontalPosition(horizontalAlign)) { }

		/// <summary>
		/// Initializes a new <see cref="EffectOrigin" /> value using a calculated position.
		/// </summary>
		/// <param name="verticalAlign">The vertical alignment used to calculate a top position.</param>
		/// <param name="horizontalAlign">The horizontal alignment use to calculate a left position.</param>
		/// <exception cref="ArgumentException">Align values must be defined.</exception>
		public EffectOrigin(VerticalAlign verticalAlign, HorizontalAlign horizontalAlign) : this(new VerticalPosition(verticalAlign), new HorizontalPosition(horizontalAlign)) { }

		#endregion


		#region Properties

		/// <summary>
		/// Gets the horizontal position.
		/// </summary>
		public HorizontalPosition Horizontal {
			get { return horizontal; }
		}

		/// <summary>
		/// Gets the vertical position.
		/// </summary>
		public VerticalPosition Vertical {
			get { return vertical; }
		}

		#endregion


		#region Methods: Object management

		/// <summary>
		/// Determines whether or not the specified object is equal to the current <see cref="EffectOrigin" /> value.
		/// </summary>
		/// <param name="obj">The <see cref="Object"/> to compare.</param>
		/// <returns>Whether or not the provided object is equal.</returns>
		public override bool Equals(object obj) {
			if(obj is EffectOrigin) return this == (EffectOrigin)obj;
			return false;
		}

		/// <summary>
		/// Determines whether or not the specified EffectOrigin is equal to the current <see cref="EffectOrigin" /> value.
		/// </summary>
		/// <param name="other">The <see cref="EffectOrigin" /> to compare.</param>
		/// <returns>Whether or not the provided <see cref="EffectOrigin" /> value is equal.</returns>
		public bool Equals(EffectOrigin other) {
			return this == other;
		}

		/// <summary>
		/// Serves as a hash function for an <see cref="EffectOrigin" />.
		/// </summary>
		/// <returns>The hash value associated with the contained data.</returns>
		public override int GetHashCode() {
			//TODO: Get TheMuuj on this guy
			return horizontal.GetHashCode() & vertical.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="String" /> that represents the current <see cref="EffectOrigin" /> value.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			return string.Format(CultureInfo.InvariantCulture, "{0},{1}", horizontal, vertical);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <returns>The client script value.</returns>
		public string ToClientSource() {
			return string.Format(CultureInfo.InvariantCulture, "[{0},{1}]", horizontal.ToClientSource(), vertical.ToClientSource());
		}

		#endregion


		#region Operators

		/// <summary>
		/// Compares the equality of two <see cref="EffectOrigin" /> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values are equal.</returns>
		public static bool operator ==(EffectOrigin value0, EffectOrigin value1) {
			return value0.vertical == value1.vertical && value0.horizontal == value1.horizontal;
		}

		/// <summary>
		/// Compares the inequality of two <see cref="EffectOrigin" /> values.
		/// </summary>
		/// <param name="value0">The first value.</param>
		/// <param name="value1">The second value.</param>
		/// <returns>Whether or not the two values are distinct.</returns>
		public static bool operator !=(EffectOrigin value0, EffectOrigin value1) {
			return value0.vertical != value1.vertical || value0.horizontal != value1.horizontal;
		}

		#endregion


	}

}